package uk.co.q3c.deplan.client.domain.util;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.Id;

import uk.co.q3c.deplan.client.domain.AbstractDomainObject;
import uk.co.q3c.deplan.client.domain.DomainKind;
import uk.co.q3c.deplan.client.domain.resource.CalendarAdjustment;
import uk.co.q3c.deplan.client.domain.resource.PersistenceReferenceException;
import uk.co.q3c.deplan.client.service.ServiceFactory;
import uk.co.q3c.deplan.client.util.Q3DateUtil;

import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Cached;
import com.googlecode.objectify.annotation.Serialized;
import com.googlecode.objectify.annotation.Unindexed;

@Unindexed
@Cached(expirationSeconds = 600)
public class CalendarAdjustments extends AbstractDomainObject implements Serializable {

	private static final long serialVersionUID = 1L;

	@Serialized
	private List<CalendarAdjustment> adjustments;
	@Id
	private Long keyId;

	private Key<? extends CalendarAdjustments> key;

	protected CalendarAdjustments() {
		super();

	}

	public CalendarAdjustments(ServiceFactory connection) {
		super(connection);
	}

	/**
	 * Creates an entry to the specified parameters. Only one entry per day may exist, so if an entry for the day already exits, the
	 * existing entry is removed and replaced by this one
	 * 
	 * @param date
	 *            the date for which the adjustment applies
	 * @param amount
	 *            the amount in minutes by which to make an adjustment
	 * @param absolute
	 *            true if the amount is an absolute value, false if it is a relative adjustment.
	 */
	public void createAdjustment(Date date, int amount, boolean absolute) {
		// TODO
		throw new RuntimeException("not yet implemented");
	}

	/**
	 * Adds the supplied value to the list against the specified date. If an entry with this date is already in the list, its previous value
	 * is displaced
	 * 
	 * @param date
	 * @param value
	 * @return the key
	 */
	public String add(Date date, CalendarAdjustment value) {
		value.setDate(date);
		int index = indexOf(date);
		if (index > -1) {
			adjustments.remove(index);
		}
		adjustments.add(value);
		return toKey(date);
	}

	public CalendarAdjustment get(String key) {
		int i = indexOf(key);
		if (i < 0) {
			return null;
		} else {
			return adjustments.get(i);
		}
	}

	public int indexOf(Date date) {
		return indexOf(toKey(date));
	}

	public int indexOf(String key) {
		if (key == null) {
			return -1;
		}
		int result = -1;
		int i = 0;
		for (CalendarAdjustment cadj : adjustments) {
			if (toKey(cadj.getDate()).equals(key)) {
				result = i;
				break;
			}
			i++;
		}
		return result;

	}

	public int size() {
		return adjustments.size();
	}

	public void remove(Date date) {

		String key = toKey(date);
		remove(key);
	}

	public void remove(String key) {
		int index = indexOf(key);
		if (index > -1) {
			adjustments.remove(index);
		}
	}

	public CalendarAdjustment get(Date date) {
		return get(toKey(date));
	}

	private String toKey(Date date) {
		return Q3DateUtil.dateToKey(date);
	}

	public void add(CalendarAdjustments additionalProfile) {
		for (CalendarAdjustment entry : additionalProfile.adjustments()) {
			adjustments.add(entry);
		}
	}

	public List<CalendarAdjustment> adjustments() {
		return adjustments;
	}

	@Override
	public void afterLoad(ServiceFactory connection) {
		super.afterLoad(connection);
		if (adjustments == null) {
			adjustments = new ArrayList<CalendarAdjustment>();
		}
	}

	@Override
	public DomainKind getKind() {
		return DomainKind.CalendarAdjustments;
	}

	public Long getKeyId() {
		return keyId;
	}

	@Override
	public Key<? extends CalendarAdjustments> getKey() throws PersistenceReferenceException {
		if (getKeyId() == null) {
			throw new PersistenceReferenceException(this.getName() + " has not been saved and cannot therefore be used as a reference");
		}
		if (key == null) {
			key = connection.resourceService()
					.keyFor(this);
		}
		return key;

	}

	@Override
	public void setKeyId(Long keyId) {
		throw new RuntimeException("Not yet implemented");
	}
}
